import Decimal from 'decimal.js';
import { cloneDeep } from 'lodash-es';
import { formatStock } from './format';

export type TenTrack = {
  time?: string;
  S5P?: number | string;
  S5V?: number | string;
  S4P?: number | string;
  S4V?: number | string;
  S3P?: number | string;
  S3V?: number | string;
  S2P?: number | string;
  S2V?: number | string;
  S1P?: number | string;
  S1V?: number | string;
  B5P?: number | string;
  B5V?: number | string;
  B4P?: number | string;
  B4V?: number | string;
  B3P?: number | string;
  B3V?: number | string;
  B2P?: number | string;
  B2V?: number | string;
  B1P?: number | string;
  B1V?: number | string;
  newest?: number | string;
  high?: number | string; // 最高价
  low?: number | string; // 最低价
  open?: number | string; // 开盘价
  volume?: number | string; // 成交量
  amount?: number | string; // 成交额
  outer?: number | string; // 外盘
  LU?: number | string; // limit up 涨停
  LD?: number | string; // Limit down 跌停
  inner?: number | string; // 内盘
  chg?: number | string;
  change?: number | string;
  [key: string]: any;
};

const MorningStartTime: string = '09:30:00'; //  早盘开盘时间
const MorningEndTime: string = '11:30:00'; //  早盘闭盘时间
const MiddayStartTime: string = '13:00:00'; //  午盘盘时间
const MiddayEndTime: string = '14:57:00'; //  午盘闭盘时间

export enum UpDownType {
  Up = 'Up',
  Down = 'Down',
  Flat = 'Flat',
}

const calcTP = (price: string | number, volume: string | number) => {
  return price && volume && price !== '-' && volume !== '-'
    ? new Decimal(price).mul(new Decimal(volume)).mul(100).toNumber()
    : '-';
};

// 趋势分析中  内外盘类型
export enum IOType {
  Inner = 'Inner', // 主内盘
  Outer = 'Outer', // 主外盘
  None = 'None', // 内外盘无成交
  Equal = 'Equal', // 内外盘差值小
}

export default class Analyze {
  originData: any[]; // 原始数据
  private stockQuotations: any = []; // 行情数据
  private fsTimes: string[] = []; // 分时成交的时间数组，用来填充行情数据时间缺失为"-"的情况
  callAuctionData: {
    inner?: number | string;
    outer?: number | string;
  } = {}; // 集合竞价数据

  private outOfTrack = {};

  private marketSnapshot: TenTrack[] = []; // 5档行情快照数组
  private fundraisingCount: number = 0; // 吸筹次数
  private shipmentCount: number = 0; // 出货次数

  constructor({ originData }: { originData: any[] }) {
    this.originData = originData;
    originData?.forEach((item: any, index: number) => {
      if (item?.details) {
        // 识别分时成交数据并转换成数组
        const details = item?.details?.map((detail: string) => detail.split(','));

        details?.forEach((detail: any) => {
          const [time, price, volume, num, type] = detail;

          this.fsTimes.push(time);
          // this.fsMap[time] = {
          //   time,
          //   price,
          //   volume,
          //   num,
          //   type,
          //   originIndex: index,
          // };
        });
      } else {
        // 非分时成交数据，即为5档详情数据
        const stock = formatStock(item);

        this.stockQuotations.push(stock);
      }
    });
    this.fillTime();
    this.generateMarketSnapshot();
    this.marketSnapshot?.forEach((snapshot, index) => {
      if (index > 0) {
        this.marketAnalysis(this.marketSnapshot[index - 1], snapshot, index);
      }
    });
    this.statistics(this.marketSnapshot);
  }

  // 从分时数据中填充时间Hms
  fillTime() {
    const len = this.stockQuotations?.length;

    this.stockQuotations.forEach((stock, index) => {
      if (!stock?.Hms && index > 0 && index < len - 1) {
        const prevHms = this.stockQuotations[index - 1]?.Hms;
        const nextHms = this.stockQuotations[index + 1]?.Hms;
        const filterHms = this.fsTimes.filter((Hms) => {
          return Hms > prevHms && (nextHms ? Hms < nextHms : true);
        });

        if (filterHms?.length > 0) {
          this.stockQuotations[index].Hms = filterHms.at(0);
        }
      }
    });
  }

  generateMarketSnapshot() {
    let tenTrack: TenTrack & { details?: any[] } = {};

    const setTenTrack = (stock: TenTrack) => {
      const genTrack = (type: 'S' | 'B', num: number) => {
        const pKey = [type, num, 'P'].join('');
        const vKey = [type, num, 'V'].join('');
        const tpKey = [type, num, 'TP'].join('');

        const price = stock?.[pKey] ?? tenTrack?.[pKey] ?? '-';
        const volume = stock?.[vKey] ?? tenTrack?.[vKey] ?? '-';

        return {
          [pKey]: price,
          [vKey]: volume,
          [tpKey]: calcTP(price, volume),
        };
      };

      tenTrack = {
        time: stock?.time ?? '-',
        ...genTrack('S', 5),
        ...genTrack('S', 4),
        ...genTrack('S', 3),
        ...genTrack('S', 2),
        ...genTrack('S', 1),

        ...genTrack('B', 1),
        ...genTrack('B', 2),
        ...genTrack('B', 3),
        ...genTrack('B', 4),
        ...genTrack('B', 5),

        newest: stock?.newest ?? tenTrack?.newest ?? '-',
        high: stock?.high ?? tenTrack?.high ?? '-',
        low: stock?.low ?? tenTrack?.low ?? '-',
        open: stock?.open ?? tenTrack?.open ?? '-',
        volume: stock?.volume ?? tenTrack?.volume ?? '-',
        amount: stock?.amount ?? tenTrack?.amount ?? '-',
        outer: stock?.outer ?? tenTrack?.outer ?? '-',
        LU: stock?.LU ?? tenTrack?.LU ?? '-',
        LD: stock?.LD ?? tenTrack?.LD ?? '-',
        inner: stock?.inner ?? tenTrack?.inner ?? '-',
        prevClose: stock?.prevClose ?? tenTrack?.prevClose ?? '-',
        chg: stock?.chg ?? tenTrack?.chg ?? '-',
        change: stock?.change ?? tenTrack?.change ?? '-',
        turn: stock?.turn ?? tenTrack?.turn ?? '-',
        Hms: stock?.Hms,
      };
    };

    // let prevTenTrack: TenTrack;
    let prevSnapshot: any;

    this.stockQuotations.forEach((stock, index) => {
      setTenTrack(stock);

      if (stock?.Hms < MorningStartTime) {
        // 时间小于开盘时间09:30:00，记录集合竞价时段的内外盘量
        this.callAuctionData.inner = tenTrack.inner;
        this.callAuctionData.outer = tenTrack.outer;
      } else if (
        (stock?.Hms >= MorningStartTime && stock?.Hms <= MorningEndTime) ||
        (stock?.Hms >= MiddayStartTime && stock?.Hms <= MiddayEndTime)
      ) {
        const snapshot = cloneDeep({ ...tenTrack });

        // 前后2次的内盘差
        const diffInner = +snapshot?.inner - prevSnapshot?.inner;
        // 前后2次的外盘差
        const diffOuter = +snapshot?.outer - prevSnapshot?.outer;

        Object.assign(snapshot, {
          ...this.calcTotalVolumeAndPrice(snapshot),
        });

        if (prevSnapshot) {
          Object.assign(snapshot, {
            diffInner,
            diffOuter,
            prevSnapshot,
            upDownType:
              snapshot.newest === prevSnapshot.newest
                ? UpDownType.Flat
                : snapshot.newest > prevSnapshot.newest
                  ? UpDownType.Up
                  : UpDownType.Down,
          });
          Object.assign(snapshot, {
            ...this.compareFiveLevels(prevSnapshot, snapshot),
            outOfTrack: cloneDeep(this.outOfTrack),
          });

          this.adjustInnerAndOuter(diffInner, diffOuter, snapshot, prevSnapshot);
        }

        this.marketSnapshot.push(snapshot);

        prevSnapshot = snapshot;
      }
    });
  }

  getMarketSnapshot() {
    return this.marketSnapshot;
  }

  // 校正内外盘差额数据（针对负数情况）
  adjustInnerAndOuter(diffInner, diffOuter, snapshot, prevSnapshot) {
    if (diffInner < 0) {
      const diff = Math.abs(diffInner);

      prevSnapshot.outer += diff;
      prevSnapshot.diffOuter += diff;
      prevSnapshot.inner += diffInner;
      prevSnapshot.diffInner += diffInner;
      snapshot.diffOuter += diffInner;
      snapshot.diffInner = 0;
    }

    if (diffOuter < 0) {
      const diff = Math.abs(diffOuter);

      prevSnapshot.inner += diff;
      prevSnapshot.diffInner += diff;
      prevSnapshot.outer += diffOuter;
      prevSnapshot.diffOuter += diffOuter;
      snapshot.diffInner += diffOuter;
      snapshot.diffOuter = 0;
    }

    if (prevSnapshot.diffInner < 0 || prevSnapshot.diffOuter < 0) {
      this.adjustInnerAndOuter(prevSnapshot.diffInner, prevSnapshot.diffOuter, prevSnapshot, prevSnapshot.prevSnapshot);
    }
  }

  // 计算当前快照买卖5档的总量和总价
  calcTotalVolumeAndPrice(snapshot) {
    const totalObj = {
      totalBuyVolume: 0,
      totalBuyAmount: 0,
      totalSellVolume: 0,
      totalSellAmount: 0,
    };

    const exp = /(S|B)\dV/g;

    const calcTotal = (key) => {
      if (exp.test(key)) {
        const tpKey = key.replace('V', 'TP');

        if (key.startsWith('S')) {
          totalObj.totalSellVolume += +(snapshot?.[key] && snapshot?.[key] !== '-' ? snapshot?.[key] : 0);
          totalObj.totalSellAmount += +(snapshot?.[tpKey] && snapshot?.[tpKey] !== '-' ? snapshot?.[tpKey] : 0);
        } else if (key.startsWith('B')) {
          totalObj.totalBuyVolume += +(snapshot?.[key] && snapshot?.[key] !== '-' ? snapshot?.[key] : 0);
          totalObj.totalBuyAmount += +(snapshot?.[tpKey] && snapshot?.[tpKey] !== '-' ? snapshot?.[tpKey] : 0);
        }
      }
    };

    Object.keys(snapshot).forEach((key: string) => {
      calcTotal(key);
    });

    return totalObj;
  }

  // 对比五档行情
  compareFiveLevels(prevSnapshot, snapshot) {
    // eslint-disable-next-line @typescript-eslint/no-shadow
    const formatToMap = (snapshot: any) => {
      const exp = /^(S|B)|\dP$/g;
      const map: any = {};

      Object.keys(snapshot).forEach((key: string) => {
        const matches = key.match(exp);

        if (matches?.length === 2) {
          map[snapshot?.[key]] = {
            price: snapshot?.[key],
            count: snapshot?.[key.replace('P', 'V')],
            type: matches?.[0],
            key,
          };
        }
      });

      return map;
    };

    const prevFiveLevels = formatToMap(prevSnapshot);
    const fiveLevels = formatToMap(snapshot);
    const disappearedLevels = {};
    const newlyLevels = {};
    const existingLevels = {};

    let minSellPrice = 10000;
    let maxSellPrice = 0;
    let minBuyPrice = 10000;
    let maxBuyPrice = 0;

    Object.keys(fiveLevels).forEach((price) => {
      const { type } = fiveLevels[price];

      if (type === 'S' && price !== '-') {
        minSellPrice = Math.min(minSellPrice, +price);
        maxSellPrice = Math.max(maxSellPrice, +price);
      } else if (type === 'B' && price !== '-') {
        minBuyPrice = Math.min(minBuyPrice, +price);
        maxBuyPrice = Math.max(maxBuyPrice, +price);
      }

      if (!prevFiveLevels[price] && this.outOfTrack[price]) {
        existingLevels[price] = {
          ...this.outOfTrack[price],
          count: fiveLevels[price].count - this.outOfTrack[price].count,
        };

        return;
      }

      if (!prevFiveLevels[price]) {
        newlyLevels[price] = { ...fiveLevels[price], Hms: snapshot.Hms };
      } else {
        if (prevFiveLevels[price].type !== fiveLevels[price].type) {
          newlyLevels[price] = { ...fiveLevels[price], Hms: snapshot.Hms };
        } else {
          existingLevels[price] = {
            ...fiveLevels[price],
            count: fiveLevels[price].count - prevFiveLevels[price].count,
            Hms: snapshot.Hms,
          };
        }
      }
    });

    Object.keys(prevFiveLevels).forEach((price) => {
      if (!fiveLevels[price]) {
        disappearedLevels[price] = { ...prevFiveLevels[price], Hms: prevSnapshot.Hms };
      } else {
        if (prevFiveLevels[price].type !== fiveLevels[price].type) {
          disappearedLevels[price] = { ...prevFiveLevels[price], Hms: prevSnapshot.Hms };
        }
      }
    });

    Object.keys(disappearedLevels).forEach((price) => {
      if (
        (+price > maxSellPrice && disappearedLevels[price].type === 'S') ||
        (+price < minBuyPrice && disappearedLevels[price].type === 'B')
      ) {
        this.outOfTrack[price] = disappearedLevels[price];
      }
    });
    // if (snapshot.upDownType === UpDownType.Down) {

    // }

    Object.keys(this.outOfTrack).forEach((price) => {
      if (
        (+price <= maxSellPrice && this.outOfTrack[price].type === 'S') ||
        (+price >= maxBuyPrice && this.outOfTrack[price].type === 'B')
      ) {
        delete this.outOfTrack[price];
      }
    });

    return { disappearedLevels, minSellPrice, maxSellPrice, minBuyPrice, maxBuyPrice, existingLevels, newlyLevels };
  }

  // 行情解析(同时生成中文解析内容)
  marketAnalysis(prevSnapshot, snapshot, index) {
    const chineseContent = [];
    const { diffInner, diffOuter, newlyLevels, existingLevels, totalSellVolume, totalBuyVolume, chg, upDownType } =
      snapshot;
    const totalVolume = diffInner + diffOuter;

    let ioType: any;

    if (totalVolume === 0) {
      chineseContent.push('内外盘均无成交，无法判断趋势');
      ioType = IOType.None;
    } else {
      const innerPercent = (diffInner / totalVolume) * 100;
      const outerPercent = (diffOuter / totalVolume) * 100;
      const diffPercent = innerPercent - outerPercent;

      if (Math.abs(diffPercent) > 10) {
        if (diffPercent > 0) {
          chineseContent.push('内盘成交量旺盛');
          ioType = IOType.Inner;
        } else {
          chineseContent.push('外盘成交量旺盛');
          ioType = IOType.Outer;
        }
      } else {
        chineseContent.push('内外盘成交量差值百分比不超过10%,无法单独判断趋势');
        ioType = IOType.Equal;
      }
    }

    const sellIncrement = totalSellVolume - prevSnapshot.totalSellVolume;
    const buyIncrement = totalBuyVolume - prevSnapshot.totalBuyVolume;
    const innerAvg = (snapshot.inner - +this.callAuctionData.inner) / (index + 1);
    const outerAvg = (snapshot.outer - +this.callAuctionData.outer) / (index + 1);

    if (totalBuyVolume / totalSellVolume >= 2 && ioType === IOType.Inner) {
      this.fundraisingCount += 1;

      const mergeData = Object.assign(newlyLevels, existingLevels);

      let pressureCount = 0;

      Object.keys(mergeData).forEach((price) => {
        const { key, count } = mergeData[price];

        if (['S1P', 'S2P'].includes(key)) {
          pressureCount += count;
        }
      });

      if (upDownType === UpDownType.Down && pressureCount > outerAvg && pressureCount > diffOuter) {
        this.fundraisingCount += 1;
      }
    }

    Object.assign(snapshot, { chineseContent, ioType });
  }

  statistics(marketSnapshot) {
    let buyAmountSum = 0;
    let sellAmountSum = 0;
    let buyVolumeSum = 0;
    let sellVolumeSum = 0;

    marketSnapshot.forEach((snapshot, index) => {
      buyAmountSum += snapshot.totalBuyAmount;
      sellAmountSum += snapshot.totalSellAmount;
      buyVolumeSum += snapshot.totalBuyVolume;
      sellVolumeSum += snapshot.totalSellVolume;
    });

    const len = marketSnapshot.length;

    console.log({
      avgBuyAmount: buyAmountSum / len,
      avgSellAmount: sellAmountSum / len,
      avgBuyVolumeRatio: buyVolumeSum / marketSnapshot.at(-1).inner,
      avgSellVolumeRatio: sellVolumeSum / marketSnapshot.at(-1).outer,
    });
  }
}
